﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using HashFoo.Rest.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using HashFoo.Rest.Server;

namespace HashFoo.Rest.ServerTests
{
    [TestClass]
    public class QueryMetaExecutorTests
    {
        class MockEnity
        {
            public string Name { get; set; }
            public int Value { get; set; }
            public int Id { get; set; }
        }

        static readonly IQueryable<MockEnity> TestDataSet =
            new EnumerableQuery<MockEnity>(new[]
            {
                new MockEnity { Id = 1, Name = "a", Value = 2 },
                new MockEnity { Id = 2, Name = "b", Value = 2 },
                new MockEnity { Id = 3, Name = "c", Value = 1 },
                new MockEnity { Id = 4, Name = "d", Value = 1 }
            });

        [TestMethod]
        public void can_apply_where_and_clause()
        {
            var meta = new QueryMeta
            {
                Filter = new FilterSet
                {
                    BinaryOp = FilterBinaryOp.And,
                    Filters = new[]
                    {
                        new Filter { PropertyName = "Name", Value = "a", Op = FilterOp.Eq },
                        new Filter { PropertyName = "Value", Op = FilterOp.Eq, Value = 1 }
                    }
                }
            };

            var nativePredicate = new Func<MockEnity, bool>(e => e.Name == "a" && e.Value == 1);

            var expectedResults = TestDataSet.Where(nativePredicate).ToArray();
            var actualResults = meta.ApplyTo(TestDataSet);

            Assert.AreEqual(expectedResults.Count(), actualResults.Count());

            var entitiesEqual = expectedResults.Zip(actualResults, (e, a) => e.Equals(a));
            Assert.IsTrue(entitiesEqual.Any(e => e == false) == false);
        }

        [TestMethod]
        public void can_apply_where_or_clause()
        {
            var meta = new QueryMeta
            {
                Filter = new FilterSet
                {
                    BinaryOp = FilterBinaryOp.Or,
                    Filters = new[]
                    {
                        new Filter { PropertyName = "Name", Value = "a", Op = FilterOp.Eq },
                        new Filter { PropertyName = "Value", Op = FilterOp.Eq, Value = 1 }
                    }
                }
            };

            var nativePredicate = new Func<MockEnity, bool>(e => e.Name == "a" || e.Value == 1);

            var expectedResults = TestDataSet.Where(nativePredicate).ToArray();
            var actualResults = meta.ApplyTo(TestDataSet);

            Assert.AreEqual(expectedResults.Length, actualResults.Count());

            var entitiesEqual = expectedResults.Zip(actualResults, (e, a) => e.Equals(a));
            Assert.IsTrue(entitiesEqual.Any(e => e == false) == false);
        }

        [TestMethod]
        public void can_apply_skip_and_take()
        {
            var meta = new QueryMeta
            {
                Skip = 1,
                Take = 2
            };

            var expectedResults = TestDataSet.Skip(meta.Skip).Take(meta.Take).ToArray();
            var actualResults = meta.ApplyTo(TestDataSet);

            Assert.AreEqual(expectedResults.Length, actualResults.Count());

            var entitiesEqual = expectedResults.Zip(actualResults, (e, a) => e.Equals(a));
            Assert.IsTrue(entitiesEqual.Any(e => e == false) == false);
        }

        [TestMethod]
        public void can_apply_single_order_asc()
        {
            var meta = new QueryMeta
            {
                SortDescriptors = new[]
                {
                    new SortDescriptor{ Direction = SortDirection.Ascending, PropertyName = "Value"},
                }
            };

            var expectedResults = TestDataSet.OrderBy(x => x.Value).ToArray();
            var actualResults = meta.ApplyTo(TestDataSet);

            Assert.AreEqual(expectedResults.Length, actualResults.Count());

            var entitiesEqual = expectedResults.Zip(actualResults, (e, a) => e.Equals(a));
            Assert.IsTrue(entitiesEqual.Any(e => e == false) == false);
        }

        [TestMethod]
        public void can_apply_multiple_orders()
        {
            var meta = new QueryMeta
            {
                SortDescriptors = new[]
                {
                    new SortDescriptor{ Direction = SortDirection.Ascending, PropertyName = "Value"},
                    new SortDescriptor { Direction = SortDirection.Descending, PropertyName = "Name"}
                }
            };

            var expectedResults = TestDataSet.OrderBy(x => x.Value).ThenByDescending(x => x.Name).ToArray();
            var actualResults = meta.ApplyTo(TestDataSet);

            Assert.AreEqual(expectedResults.Length, actualResults.Count());

            var entitiesEqual = expectedResults.Zip(actualResults, (e, a) => e.Equals(a));
            Assert.IsTrue(entitiesEqual.Any(e => e == false) == false);
        }
    }
}
